package ms.jasim.model.presentation;

import java.util.ArrayList;
import java.util.List;

import ms.jasim.framework.IDescriptorProvider;
import ms.jasim.framework.JasimFramework;
import ms.jasim.model.Actor;
import ms.jasim.model.Capability;
import ms.jasim.model.CustomizableObject;
import ms.jasim.model.Goal;
import ms.jasim.model.JasimModel;
import ms.jasim.model.items.Item;
import ms.jasim.model.items.PropertyDescriptor;
import ms.jasim.model.items.PropertyList;

import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.edit.provider.IChangeNotifier;
import org.eclipse.emf.edit.provider.INotifyChangedListener;
import org.eclipse.emf.edit.ui.provider.NotifyChangedToViewerRefresh;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;

public class CustomizableObjectTableViewer extends TableViewer implements INotifyChangedListener {

	public enum ItemType {
		Unknown, IntItem, TextItem, GoalItem, ActorItem, GoalListItem, ActorListItem
	}
	
	public class MyContentProvider implements IStructuredContentProvider {

		public MyContentProvider() {
		}
		
		@Override
		public Object[] getElements(Object input) {
			if (input instanceof XMIResource) {
				EList<EObject> contents = ((XMIResource) input).getContents();
				if (contents.size() > 0 && contents.get(0) instanceof JasimModel) {
					JasimModel model = (JasimModel) contents.get(0);
					
					if (Actor.class == clazz) {
						defaultProperties = model.getDefaultActorProperties();
						return model.getActors().getActor().toArray();
					}
					else if (Goal.class == clazz) {
						defaultProperties = model.getDefaultGoalProperties();
						return model.getGoals().getGoal().toArray();
					}
					else if (Capability.class == clazz) {
						defaultProperties = model.getDefaultCapabilityProperties();
						ArrayList<Capability> list = new ArrayList<Capability>();
						for (Actor a: model.getActors())
							list.addAll(a.getCapability());
						return list.toArray();
					}
				}
			}
			return null;
		}

		@Override
		public void dispose() {
			if (adapterFactory instanceof IChangeNotifier)
				((IChangeNotifier) adapterFactory).removeListener(CustomizableObjectTableViewer.this);			
		}

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			// TODO Auto-generated method stub
			
		}
		
	}

	public static abstract class AbstractEditingSupport extends EditingSupport {

		private CellEditor editor;

		public AbstractEditingSupport(TableViewer viewer, CellEditor editor) {
			super(viewer);
			this.editor = editor != null ? editor : new TextCellEditor((viewer).getTable());
		}
		
		@Override
		protected boolean canEdit(Object element) {
			return true;
		}

		@Override
		protected CellEditor getCellEditor(Object element) {
			return editor;
		}

		@Override
		protected Object getValue(Object element) {
			CustomizableObject obj = (CustomizableObject) element;
			return getValue(obj);
		}

		protected abstract Object getValue(CustomizableObject obj);
		
		protected abstract void setValue(CustomizableObject obj, Object value);
		
		@Override
		protected void setValue(Object element, Object value) {
			CustomizableObject obj = (CustomizableObject) element;
			setValue(obj, value);
		}

	}
	
	public static class DescriptorEditingSupport extends AbstractEditingSupport {

		private PropertyDescriptor descriptor;

		public DescriptorEditingSupport(CustomizableObjectTableViewer viewer, CellEditor editor, PropertyDescriptor d) {
			super(viewer, editor);
			this.descriptor = d;
		}

		public  ItemType getItemType(Item item) {
			ItemType res = ItemType.Unknown;
			try {
				String name = item.getClass().getName();
				int idx = name.lastIndexOf('.');
				if (idx >=0)
					name = name.substring(idx + 1);
				if (name.endsWith("Impl"))
					name = name.substring(0, name.length() - 4);
				res = ItemType.valueOf(name);
			}
			catch (Exception e) {
				// there no corresponding ItemType for this item
			}
			return res;
		}
		
		@Override
		protected Object getValue(CustomizableObject obj) {
			Object value = obj.getProperties().get(descriptor.getName(), 
					((CustomizableObjectTableViewer)getViewer()).defaultProperties, 
					descriptor).getValue(null);
			return value != null ? value.toString() : "";
		}

		@Override
		protected void setValue(CustomizableObject obj, Object value) {
			if (descriptor != null) {
				Item item = obj.getProperties().get(descriptor.getName());
				Item resolvedItem = obj.getProperties().get(descriptor.getName(), 
						((CustomizableObjectTableViewer)getViewer()).defaultProperties, 
						descriptor);
				
				if (value instanceof String) {
					String sValue = (String) value;
					
					if (sValue.length() == 0 && item != null) // remove current property
						obj.getProperties().getItem().remove(item);
					if (!sValue.equalsIgnoreCase(resolvedItem.getValue(null).toString())) {
						if (item == null) {
							item = (Item) EcoreUtil.copy(descriptor.getItem());
							item.setReservedName(true);
							obj.getProperties().getItem().add(item);
						}
						try {
							item.setValue(null, value);
						} catch (Exception e) {	}
					}
				}
			}
			getViewer().update(obj, null);
		}
		
	}
	
	public static class MyLabelProvider extends LabelProvider implements ITableLabelProvider {

		private CustomizableObjectTableViewer viewer;

		public MyLabelProvider(CustomizableObjectTableViewer viewer) {
			this.viewer = viewer;
		}
		
		@Override
		public Image getColumnImage(Object element, int columnIndex) {
			// TODO Auto-generated method stub
			return null;
		}
		
		@Override
		public String getColumnText(Object element, int columnIndex) {
			if (element instanceof CustomizableObject) {
				CustomizableObject obj = (CustomizableObject) element;
				return getColumnText(obj, columnIndex);
			}
			return "";
		}

		protected String getColumnText(CustomizableObject obj, int columnIndex) {
			Object data = viewer.getTable().getColumn(columnIndex).getData();
			if (data instanceof PropertyDescriptor) {
				PropertyDescriptor descriptor = (PropertyDescriptor) data;
				Object value = obj.getProperties().get(descriptor.getName(), viewer.defaultProperties, descriptor).getValue(null);
				return value != null ? value.toString() : null;
			}
			return null;
		}
		
		@Override
		public String getText(Object element) {
			if (element instanceof XMIResource)
				return "";
			return super.getText(element);
		}
	}
	
	private Class<?> clazz;
	private List<PropertyDescriptor> descriptors;
	private PropertyList defaultProperties;
	private AdapterFactory adapterFactory;
	
	public CustomizableObjectTableViewer(Composite parent, int style, Class<?> clazz) {
		super(parent, style);
		
		this.clazz = clazz;
		
		Table table = getTable();
		TableLayout layout = new TableLayout();
		table.setLayout(layout);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		CellNavigation.createNavigation(this);

		createColumns(clazz, layout);
		
		setContentProvider(new MyContentProvider());
	}

	protected void createColumns(Class<?> clazz, TableLayout layout) {
		/*TableViewerColumn nameColumn = new TableViewerColumn(this, SWT.NONE);
		layout.addColumnData(new ColumnWeightData(3, 70, true));
		nameColumn.getColumn().setText("Name");
		nameColumn.getColumn().setResizable(true);
		nameColumn.setEditingSupport(new TextEditingSupport(this, 0, null));
		
		TableViewerColumn textColumn = new TableViewerColumn(this, SWT.NONE);
		layout.addColumnData(new ColumnWeightData(3, 100, true));
		textColumn.getColumn().setText("Text");
		textColumn.getColumn().setResizable(true);
		textColumn.setEditingSupport(new MyEditingSupport(this, 1, null));*/
		
		IDescriptorProvider descriptorProvider = JasimFramework.getDescriptorProvider(null);
		if (descriptorProvider != null) {
			descriptors = descriptorProvider.getPropertyDescriptors(clazz);
			for (PropertyDescriptor d: descriptors) {
				TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
				layout.addColumnData(new ColumnWeightData(3, 50, true));
				column.getColumn().setText(d.getName());
				column.getColumn().setResizable(true);
				column.getColumn().setData(d);
				column.setEditingSupport(new DescriptorEditingSupport(this, null, d));
			}
		}
	}
	
	@Override
	public void notifyChanged(Notification notification) {
		if (this.getControl() != null && !this.getControl().isDisposed())
	    {
	        NotifyChangedToViewerRefresh.handleNotifyChanged(
	          this,
	          notification.getNotifier(),
	          notification.getEventType(),
	          notification.getFeature(),
	          notification.getOldValue(),
	          notification.getNewValue(),
	          notification.getPosition());
	    }
	}

	public void setAdapterFactory(AdapterFactory adapterFactory) {
		this.adapterFactory = adapterFactory;
		if (adapterFactory instanceof IChangeNotifier)
			((IChangeNotifier) adapterFactory).addListener(this);
	}
}
